import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.Map;
import java.lang.reflect.Constructor;
import java.net.*;

public class Client extends UnicastRemoteObject implements Client_itf {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private static Map<Integer, SharedObject> listeSO;
	private static Server_itf s;
	private static Client_itf c;
	
    public Client() throws RemoteException {
        super();
    }


    ///////////////////////////////////////////////////
    //         Interface to be used by applications
    ///////////////////////////////////////////////////

    // initialization of the client layer
    public static void init() {
        try {
        	if(c == null) {
        		c = new Client();
        		s = (Server_itf) Naming.lookup("//localhost:2011/Serveur");
        		listeSO = new HashMap<Integer, SharedObject>();
        	}
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (NotBoundException e) {
			e.printStackTrace();
		}
    }
	
    // lookup in the name server
    public static SharedObject lookup(String name) {
		try {
			// StubID : classe avec un ID et un stub
			int id = s.lookup(name);
			if (id != -1) {	
				/*
				 *  l'objet existe dans le serveur,
				 *  on l'ajoute, initialisé à null dans notre liste
				 *  pour savoir qu'il existe
				 */
				Object o = lock_read(id);
				GenerateurStub.ecrireFichier(o); // on cree + compile le fichier de stub
				SharedObject objRecherche = GenerateurStub.instance(id, o); // on l'instancie
				listeSO.put(id, objRecherche);
				objRecherche.unlock();
				return objRecherche;
			}
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		return null;
    }
	
    /*
     * Enregistre l'objet passé en paramètre dans le serveur
     * L'ajoute à notre liste de SharedObject
     */
    public static void register(String name, SharedObject_itf so) {
       	try {
			s.register(name, so.getId());
			listeSO.put(so.getId(), (SharedObject) so);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
    }

    // creation of a shared object
    public static SharedObject create(Object o) {
		try {
			GenerateurStub.ecrireFichier(o);	// On cree et on compile le fichier de stub
			System.out.println("On vient de creer un objet : "+o.getClass().getSimpleName());
			int id = s.create(o);
			return GenerateurStub.instance(id,o);	// On retourne l'instance de stub au client
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
    }
	
    /////////////////////////////////////////////////////////////
    //    Interface to be used by the consistency protocol
    ////////////////////////////////////////////////////////////

    // request a read lock from the server
    public static Object lock_read(int id) {
    	try {
			return s.lock_read(id, c);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		return null;
    }

    // request a write lock from the server
    public static Object lock_write (int id) {
    	try {
			return s.lock_write(id, c);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		return null;
    }

    // receive a lock reduction request from the server
    public Object reduce_lock(int id) throws java.rmi.RemoteException {
    	SharedObject so = listeSO.get(id);
    	so.reduce_lock();
    	return so.obj;	/*
    					*	on retourne la valeur de l'objet au ServerObject
    					*	pour que celui ci se mette a jour
    					*/ 
    }


    // receive a reader invalidation request from the server
    public void invalidate_reader(int id) throws RemoteException {
    	SharedObject so = listeSO.get(id);
    	so.invalidate_reader();
    }


    // receive a writer invalidation request from the server
    public Object invalidate_writer(int id) throws RemoteException {
    	SharedObject so = listeSO.get(id);
    	so.invalidate_writer();
    	return so.obj;
    }
}
